/**
 * @fileoverview 百度地图的测距工具类，对外开放。
 * 允许用户在地图上点击完成距离的测量。
 * 使用者可以自定义测距线段的相关样式，例如线宽、颜色、测距结果所用的单位制等等。
 * 主入口类是<a href="symbols/BMapLib.DistanceTool.html">DistanceTool</a>，
 * 基于Baidu Map API 1.2。
 *
 * @author Baidu Map Api Group
 * @version 1.2
 */

/**
 * @namespace BMap的所有library类均放在BMapLib命名空间下
 */
var BMapLib = window.BMapLib = BMapLib || {};

(function () {
    /**
     * 声明baidu包
     */
    var baidu = baidu || { guid: '$BAIDU$' };
    (function () {
        // 一些页面级别唯一的属性，需要挂载在window[baidu.guid]上
        window[baidu.guid] = {}

        /**
         * 将源对象的所有属性拷贝到目标对象中
         * @name baidu.extend
         * @function
         * @grammar baidu.extend(target, source)
         * @param {Object} target 目标对象
         * @param {Object} source 源对象
         * @returns {Object} 目标对象
         */
        baidu.extend = function (target, source) {
            for (var p in source) {
                if (source.hasOwnProperty(p)) {
                    target[p] = source[p]
                }
            }
            return target
        }

        /**
         * @ignore
         * @namespace
         * @baidu.lang 对语言层面的封装，包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
         * @property guid 对象的唯一标识
        */
        baidu.lang = baidu.lang || {}

        /**
         * 返回一个当前页面的唯一标识字符串。
         * @function
         * @grammar baidu.lang.guid()
         * @returns {String} 当前页面的唯一标识字符串
         */
        baidu.lang.guid = function () {
            return 'TANGRAM__' + (window[baidu.guid]._counter++).toString(36)
        }

        window[baidu.guid]._counter = window[baidu.guid]._counter || 1

        /**
         * 所有类的实例的容器
         * key为每个实例的guid
         */
        window[baidu.guid]._instances = window[baidu.guid]._instances || {}

        /**
         * Tangram继承机制提供的一个基类，用户可以通过继承baidu.lang.Class来获取它的属性及方法。
         * @function
         * @name baidu.lang.Class
         * @grammar baidu.lang.Class(guid)
         * @param {string} guid	对象的唯一标识
         * @meta standard
         * @remark baidu.lang.Class和它的子类的实例均包含一个全局唯一的标识guid。
         * guid是在构造函数中生成的，因此，继承自baidu.lang.Class的类应该直接或者间接调用它的构造函数。<br>
         * baidu.lang.Class的构造函数中产生guid的方式可以保证guid的唯一性，及每个实例都有一个全局唯一的guid。
         */
        baidu.lang.Class = function (guid) {
            this.guid = guid || baidu.lang.guid()
            window[baidu.guid]._instances[this.guid] = this
        }

        window[baidu.guid]._instances = window[baidu.guid]._instances || {}

        /**
         * 判断目标参数是否string类型或String对象
         * @name baidu.lang.isString
         * @function
         * @grammar baidu.lang.isString(source)
         * @param {Any} source 目标参数
         * @shortcut isString
         * @meta standard
         *
         * @returns {boolean} 类型判断结果
         */
        baidu.lang.isString = function (source) {
            return Object.prototype.toString.call(source) == '[object String]'
        }

        /**
         * 判断目标参数是否为function或Function实例
         * @name baidu.lang.isFunction
         * @function
         * @grammar baidu.lang.isFunction(source)
         * @param {Any} source 目标参数
         * @returns {boolean} 类型判断结果
         */
        baidu.lang.isFunction = function (source) {
            return Object.prototype.toString.call(source) == '[object Function]'
        }

        /**
         * 重载了默认的toString方法，使得返回信息更加准确一些。
         * @return {string} 对象的String表示形式
         */
        baidu.lang.Class.prototype.toString = function () {
            return '[object ' + (this._className || 'Object') + ']'
        }

        /**
         * 释放对象所持有的资源，主要是自定义事件。
         * @name dispose
         * @grammar obj.dispose()
         */
        baidu.lang.Class.prototype.dispose = function () {
            delete window[baidu.guid]._instances[this.guid]
            for (var property in this) {
                if (!baidu.lang.isFunction(this[property])) {
                    delete this[property]
                }
            }
            this.disposed = true
        }

        /**
         * 自定义的事件对象。
         * @function
         * @name baidu.lang.Event
         * @grammar baidu.lang.Event(type[, target])
         * @param {string} type	 事件类型名称。为了方便区分事件和一个普通的方法，事件类型名称必须以"on"(小写)开头。
         * @param {Object} [target]触发事件的对象
         * @meta standard
         * @remark 引入该模块，会自动为Class引入3个事件扩展方法：addEventListener、removeEventListener和dispatchEvent。
         * @see baidu.lang.Class
         */
        baidu.lang.Event = function (type, target) {
            this.type = type
            this.returnValue = true
            this.target = target || null
            this.currentTarget = null
        }

        /**
         * 注册对象的事件监听器。引入baidu.lang.Event后，Class的子类实例才会获得该方法。
         * @grammar obj.addEventListener(type, handler[, key])
         * @param 	{string}   type         自定义事件的名称
         * @param 	{Function} handler      自定义事件被触发时应该调用的回调函数
         * @param 	{string}   [key]		为事件监听函数指定的名称，可在移除时使用。如果不提供，方法会默认为它生成一个全局唯一的key。
         * @remark 	事件类型区分大小写。如果自定义事件名称不是以小写"on"开头，该方法会给它加上"on"再进行判断，即"click"和"onclick"会被认为是同一种事件。
         */
        baidu.lang.Class.prototype.addEventListener = function (type, handler, key) {
            if (!baidu.lang.isFunction(handler)) {
                return
            }
            !this.__listeners && (this.__listeners = {})
            var t = this.__listeners; var id
            if (typeof key === 'string' && key) {
                if (/[^\w\-]/.test(key)) {
                    throw ('nonstandard key:' + key)
                } else {
                    handler.hashCode = key
                    id = key
                }
            }
            type.indexOf('on') != 0 && (type = 'on' + type)
            typeof t[type] !== 'object' && (t[type] = {})
            id = id || baidu.lang.guid()
            handler.hashCode = id
            t[type][id] = handler
        }

        /**
         * 移除对象的事件监听器。引入baidu.lang.Event后，Class的子类实例才会获得该方法。
         * @grammar obj.removeEventListener(type, handler)
         * @param {string}   type     事件类型
         * @param {Function|string} handler  要移除的事件监听函数或者监听函数的key
         * @remark 	如果第二个参数handler没有被绑定到对应的自定义事件中，什么也不做。
         */
        baidu.lang.Class.prototype.removeEventListener = function (type, handler) {
            if (baidu.lang.isFunction(handler)) {
                handler = handler.hashCode
            } else if (!baidu.lang.isString(handler)) {
                return
            }
            !this.__listeners && (this.__listeners = {})
            type.indexOf('on') != 0 && (type = 'on' + type)
            var t = this.__listeners
            if (!t[type]) {
                return
            }
            t[type][handler] && delete t[type][handler]
        }

        /**
         * 派发自定义事件，使得绑定到自定义事件上面的函数都会被执行。引入baidu.lang.Event后，Class的子类实例才会获得该方法。
         * @grammar obj.dispatchEvent(event, options)
         * @param {baidu.lang.Event|String} event 	Event对象，或事件名称(1.1.1起支持)
         * @param {Object} options 扩展参数,所含属性键值会扩展到Event对象上(1.2起支持)
         * @remark 处理会调用通过addEventListenr绑定的自定义事件回调函数之外，还会调用直接绑定到对象上面的自定义事件。
         * 例如：<br>
         * myobj.onMyEvent = function(){}<br>
         * myobj.addEventListener("onMyEvent", function(){});
         */
        baidu.lang.Class.prototype.dispatchEvent = function (event, options) {
            if (baidu.lang.isString(event)) {
                event = new baidu.lang.Event(event)
            }
            !this.__listeners && (this.__listeners = {})
            options = options || {}
            for (var i in options) {
                event[i] = options[i]
            }
            var i; var t = this.__listeners; var p = event.type
            event.target = event.target || this
            event.currentTarget = this
            p.indexOf('on') != 0 && (p = 'on' + p)
            baidu.lang.isFunction(this[p]) && this[p].apply(this, arguments)
            if (typeof t[p] === 'object') {
                for (i in t[p]) {
                    t[p][i].apply(this, arguments)
                }
            }
            return event.returnValue
        }

        /**
         * 为类型构造器建立继承关系
         * @name baidu.lang.inherits
         * @function
         * @grammar baidu.lang.inherits(subClass, superClass[, className])
         * @param {Function} subClass 子类构造器
         * @param {Function} superClass 父类构造器
         * @param {string} className 类名标识
         * @remark 使subClass继承superClass的prototype，
         * 因此subClass的实例能够使用superClass的prototype中定义的所有属性和方法。<br>
         * 这个函数实际上是建立了subClass和superClass的原型链集成，并对subClass进行了constructor修正。<br>
         * <strong>注意：如果要继承构造函数，需要在subClass里面call一下，具体见下面的demo例子</strong>
         * @shortcut inherits
         * @meta standard
         * @see baidu.lang.Class
         */
        baidu.lang.inherits = function (subClass, superClass, className) {
            var key; var proto
                var selfProps = subClass.prototype
                var clazz = new Function()
            clazz.prototype = superClass.prototype
            proto = subClass.prototype = new clazz()
            for (key in selfProps) {
                proto[key] = selfProps[key]
            }
            subClass.prototype.constructor = subClass
            subClass.superClass = superClass.prototype

            if (typeof className === 'string') {
                proto._className = className
            }
        }

        /**
         * @ignore
         * @namespace baidu.dom 操作dom的方法。
         */
        baidu.dom = baidu.dom || {}

        /**
         * 从文档中获取指定的DOM元素
         *
         * @param {string|HTMLElement} id 元素的id或DOM元素
         * @meta standard
         * @return {HTMLElement} DOM元素，如果不存在，返回null，如果参数不合法，直接返回参数
         */
        baidu._g = baidu.dom._g = function (id) {
            if (baidu.lang.isString(id)) {
                return document.getElementById(id)
            }
            return id
        }

        /**
         * 从文档中获取指定的DOM元素
         * @name baidu.dom.g
         * @function
         * @grammar baidu.dom.g(id)
         * @param {string|HTMLElement} id 元素的id或DOM元素
         * @meta standard
         *
         * @returns {HTMLElement|null} 获取的元素，查找不到时返回null,如果参数不合法，直接返回参数
         */
        baidu.g = baidu.dom.g = function (id) {
            if (typeof id === 'string' || id instanceof String) {
                return document.getElementById(id)
            } else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
                return id
            }
            return null
        }

        /**
         * 在目标元素的指定位置插入HTML代码
         * @name baidu.dom.insertHTML
         * @function
         * @grammar baidu.dom.insertHTML(element, position, html)
         * @param {HTMLElement|string} element 目标元素或目标元素的id
         * @param {string} position 插入html的位置信息，取值为beforeBegin,afterBegin,beforeEnd,afterEnd
         * @param {string} html 要插入的html
         * @remark
         *
         * 对于position参数，大小写不敏感<br>
         * 参数的意思：beforeBegin&lt;span&gt;afterBegin   this is span! beforeEnd&lt;/span&gt; afterEnd <br />
         * 此外，如果使用本函数插入带有script标签的HTML字符串，script标签对应的脚本将不会被执行。
         *
         * @shortcut insertHTML
         * @meta standard
         *
         * @returns {HTMLElement} 目标元素
         */
        baidu.insertHTML = baidu.dom.insertHTML = function (element, position, html) {
            element = baidu.dom.g(element)
            var range, begin

            if (element.insertAdjacentHTML) {
                element.insertAdjacentHTML(position, html)
            } else {
                // 这里不做"undefined" != typeof(HTMLElement) && !window.opera判断，其它浏览器将出错？！
                // 但是其实做了判断，其它浏览器下等于这个函数就不能执行了
                range = element.ownerDocument.createRange()
                // FF下range的位置设置错误可能导致创建出来的fragment在插入dom树之后html结构乱掉
                // 改用range.insertNode来插入html, by wenyuxiang @ 2010-12-14.
                position = position.toUpperCase()
                if (position == 'AFTERBEGIN' || position == 'BEFOREEND') {
                    range.selectNodeContents(element)
                    range.collapse(position == 'AFTERBEGIN')
                } else {
                    begin = position == 'BEFOREBEGIN'
                    range[begin ? 'setStartBefore' : 'setEndAfter'](element)
                    range.collapse(begin)
                }
                range.insertNode(range.createContextualFragment(html))
            }
            return element
        }

        /**
         * 为目标元素添加className
         * @name baidu.dom.addClass
         * @function
         * @grammar baidu.dom.addClass(element, className)
         * @param {HTMLElement|string} element 目标元素或目标元素的id
         * @param {string} className 要添加的className，允许同时添加多个class，中间使用空白符分隔
         * @remark
         * 使用者应保证提供的className合法性，不应包含不合法字符，className合法字符参考：http://www.w3.org/TR/CSS2/syndata.html。
         * @shortcut addClass
         * @meta standard
         *
         * @returns {HTMLElement} 目标元素
         */
        baidu.ac = baidu.dom.addClass = function (element, className) {
            element = baidu.dom.g(element)
            var classArray = className.split(/\s+/)
                var result = element.className
                var classMatch = ' ' + result + ' '
                var i = 0
                var l = classArray.length

            for (; i < l; i++) {
                 if (classMatch.indexOf(' ' + classArray[i] + ' ') < 0) {
                     result += (result ? ' ' : '') + classArray[i]
                 }
            }

            element.className = result
            return element
        }

        /**
         * @ignore
         * @namespace baidu.event 屏蔽浏览器差异性的事件封装。
         * @property target 	事件的触发元素
         * @property pageX 		鼠标事件的鼠标x坐标
         * @property pageY 		鼠标事件的鼠标y坐标
         * @property keyCode 	键盘事件的键值
         */
        baidu.event = baidu.event || {}

        /**
         * 事件监听器的存储表
         * @private
         * @meta standard
         */
        baidu.event._listeners = baidu.event._listeners || []

        /**
         * 为目标元素添加事件监听器
         * @name baidu.event.on
         * @function
         * @grammar baidu.event.on(element, type, listener)
         * @param {HTMLElement|string|window} element 目标元素或目标元素id
         * @param {string} type 事件类型
         * @param {Function} listener 需要添加的监听器
         * @remark
         *  1. 不支持跨浏览器的鼠标滚轮事件监听器添加<br>
         *  2. 改方法不为监听器灌入事件对象，以防止跨iframe事件挂载的事件对象获取失败
         * @shortcut on
         * @meta standard
         * @see baidu.event.un
         *
         * @returns {HTMLElement|window} 目标元素
         */
        baidu.on = baidu.event.on = function (element, type, listener) {
            type = type.replace(/^on/i, '')
            element = baidu._g(element)
            var realListener = function (ev) {
                // 1. 这里不支持EventArgument,  原因是跨frame的事件挂载
                // 2. element是为了修正this
                listener.call(element, ev)
            }
            var lis = baidu.event._listeners
            var filter = baidu.event._eventFilter
            var afterFilter
            var realType = type
            type = type.toLowerCase()
            // filter过滤
            if (filter && filter[type]) {
                afterFilter = filter[type](element, type, realListener)
                realType = afterFilter.type
                realListener = afterFilter.listener
            }
            // 事件监听器挂载
            if (element.addEventListener) {
                element.addEventListener(realType, realListener, false)
            } else if (element.attachEvent) {
                element.attachEvent('on' + realType, realListener)
            }

            // 将监听器存储到数组中
            lis[lis.length] = [element, type, listener, realListener, realType]
            return element
        }

        /**
         * 为目标元素移除事件监听器
         * @name baidu.event.un
         * @function
         * @grammar baidu.event.un(element, type, listener)
         * @param {HTMLElement|string|window} element 目标元素或目标元素id
         * @param {string} type 事件类型
         * @param {Function} listener 需要移除的监听器
         * @shortcut un
         * @meta standard
         *
         * @returns {HTMLElement|window} 目标元素
         */
        baidu.un = baidu.event.un = function (element, type, listener) {
            element = baidu._g(element)
            type = type.replace(/^on/i, '').toLowerCase()

            var lis = baidu.event._listeners
                var len = lis.length
                var isRemoveAll = !listener
                var item
                var realType; var realListener

            // 如果将listener的结构改成json
            // 可以节省掉这个循环，优化性能
            // 但是由于un的使用频率并不高，同时在listener不多的时候
            // 遍历数组的性能消耗不会对代码产生影响
            // 暂不考虑此优化
            while (len--) {
                item = lis[len]

                // listener存在时，移除element的所有以listener监听的type类型事件
                // listener不存在时，移除element的所有type类型事件
                if (item[1] === type &&
                    item[0] === element &&
                    (isRemoveAll || item[2] === listener)) {
                    realType = item[4]
                    realListener = item[3]
                    if (element.removeEventListener) {
                        element.removeEventListener(realType, realListener, false)
                    } else if (element.detachEvent) {
                        element.detachEvent('on' + realType, realListener)
                    }
                    lis.splice(len, 1)
                }
            }
            return element
        }

        /**
         * 阻止事件的默认行为
         * @name baidu.event.preventDefault
         * @function
         * @grammar baidu.event.preventDefault(event)
         * @param {Event} event 事件对象
         * @meta standard
         */
        baidu.preventDefault = baidu.event.preventDefault = function (event) {
           if (event.preventDefault) {
               event.preventDefault()
           } else {
               event.returnValue = false
           }
        }
    })()

    /**
     * @exports DistanceTool as BMapLib.DistanceTool
     */
    var DistanceTool =
        /**
         * DistanceTool类的构造函数
         * @class 距离测算类，实现测距效果的<b>入口</b>。
         * 实例化该类后，即可调用该类提供的open
         * 方法开启测距状态。
         *
         * @constructor
         * @param {Map} map Baidu map的实例对象
         * @param {Json Object} opts 可选的输入参数，非必填项。可输入选项包括：<br />
         * {"<b>followText</b>" : {String} 测距过程中，提示框文字,
         * <br />"<b>unit</b>" : {String} 测距结果所用的单位制，可接受的属性为"metric"表示米制和"us"表示美国传统单位,
         * <br />"<b>lineColor</b>" : {String} 折线颜色,
         * <br />"<b>lineStroke</b>" : {Number} 折线宽度,
         * <br />"<b>opacity</b>" : {Number} 透明度,
         * <br />"<b>lineStyle</b>" : {String} 折线的样式，只可设置solid和dashed,
         * <br />"<b>secIcon</b>" : {BMap.Icon} 转折点的Icon,
         * <br />"<b>closeIcon</b>" : {BMap.Icon} 关闭按钮的Icon,
         * <br />"<b>cursor</b>" : {String} 跟随的鼠标样式}
         *
         * @example <b>参考示例：</b><br />
         * var map = new BMap.Map("container");<br />map.centerAndZoom(new BMap.Point(116.404, 39.915), 15);<br />var myDistanceToolObject = new BMapLib.DistanceTool(map, {lineStroke : 2});
         */
        BMapLib.DistanceTool = function (map, opts) {
            if (!map) {
                return
            }

            /**
             * map对象
             * @private
             * @type {Map}
             */
            this._map = map

            opts = opts || {}
            /**
             * _opts是默认参数赋值。
             * 下面通过用户输入的opts，对默认参数赋值
             * @private
             * @type {Json}
             */
            this._opts = baidu.extend(
                baidu.extend(this._opts || {}, {

                    /**
                     * 测距提示
                     * @private
                     * @type {String}
                     */
                    tips: '测距',

                    /**
                     * 测距过程中，提示框文字
                     * @private
                     * @type {String}
                     */
                    followText: '单击确定地点，双击结束',

                    /**
                     * 测距结果所用的单位制，可接受的属性为"metric"表示米制和"us"表示美国传统单位
                     * @private
                     * @type {String}
                     */
                    unit: 'metric',

                    /**
                     * 折线颜色
                     * @private
                     * @type {String}
                     */
                    lineColor: '#ff6319',

                    /**
                     * 折线线宽
                     * @private
                     * @type {Number}
                     */
                    lineStroke: 2,

                    /**
                     * 折线透明度
                     * @private
                     * @type {Number}
                     */
                    opacity: 0.8,

                    /**
                     * 折线样式
                     * @private
                     * @type {String}
                     */
                    lineStyle: 'solid',

                    /**
                     * 跟随鼠标样式
                     * @private
                     * @type {String}
                     */
                    cursor: '/static/bmap/images/ruler.cur',

                    /**
                     * 转折点的ICON样式
                     * @private
                     * @type {BMap.Icon}
                     */
                     secIcon: null,

                    /**
                     * 转折点的ICON样式
                     * @private
                     * @type {BMap.Icon}
                     */
                     closeIcon: null
                })
            , opts)

            /**
             * 跟随的title覆盖物
             * @private
             * @type {BMap.Label}
             */
            this._followTitle = null

            /**
             * 折线包含所有点的数组
             * @private
             * @type {Array}
             */
            this._points = []

            /**
             * 折线所包含的所有path数组
             * @private
             * @type {Array}
             */
            this._paths = []

            /**
             * 折线结点图片数组
             * @private
             * @type {Array}
             */
            this._dots = []

            /**
             * 折线测距包含所有线段的距离
             * @private
             * @type {Array}
             */
            this._segDistance = []

            /**
             * 覆盖物的数组
             * @private
             * @type {Array}
             */
            this._overlays = []

            /**
             * 是否在调用map.clearOverlays清除画线需要建立的相关overlay元素
             * @private
             * @type {Boolean}
             */
            this._enableMassClear = true,

            /**
             * 单位制，存储语言包中定义的单位名称
             * @private
             * @type {Json}
             */
            this._units = {
                // metric 表示米制
                metric: {
                    /**
                     * 米制的名称
                     * @type {String}
                     */
                    name: 'metric',

                    /**
                     * 和米制的换算关系
                     * @type {Number}
                     */
                    conv: 1,

                    /**
                     * 米制单位下两个单位制之间的换算关系
                     * @type {Number}
                     */
                    incon: 1000,

                    /**
                     * 米制单位下较小单位
                     * @type {String}
                     */
                    u1: '米',

                    /**
                     * 米制单位下较大单位
                     * @type {String}
                     */
                    u2: '公里'
                },
                // us 表示美国传统单位，各参数意义同上metric
                us: {
                    name: 'us',
                    conv: 3.2808,
                    incon: 5279.856,
                    u1: '英尺',
                    u2: '英里'
                }
            }

            /**
             * 是否已经开启了测距状态
             * @private
             * @type {Boolean}
             */
            this._isOpen = false

            /**
             * 未点击任何一点时，鼠标移动时的跟随提示文字
             * @private
             * @type {String}
             */
            this._startFollowText = '单击确定起点'

            /**
             * 地图移动的计时器
             * @private
             * @type {Object}
             */
            this._movingTimerId = null

            /**
             * 测距需要添加的CSS样式
             * @private
             * @type {Json}
             */
             this._styles = {
                 'BMapLib_diso': 'height:17px;width:5px;position:absolute;background:url(/static/bmap/images/dis_box_01.gif) no-repeat left top',
                  'BMapLib_disi': 'color:#7a7a7a;position:absolute;left:5px;padding:0 4px 1px 0;line-height:17px;background:url(/static/bmap/images/dis_box_01.gif) no-repeat right top',
                  'BMapLib_disBoxDis': 'color:#ff6319;font-weight:bold'
             }

            if (this._opts.lineStroke <= 0) {
                this._opts.lineStroke = 2
            }
            if (this._opts.opacity > 1) {
                this._opts.opacity = 1
            } else if (this._opts.opacity < 0) {
                this._opts.opacity = 0
            }
            if (this._opts.lineStyle != 'solid' &&
                this._opts.lineStyle != 'dashed') {
                    this._opts.lineStyle = 'solid'
            }
            if (!this._units[this._opts.unit]) {
                this._opts.unit = 'metric'
            }

            this.text = '测距'
        }

    // 通过baidu.lang下的inherits方法，让DistanceTool继承baidu.lang.Class
    baidu.lang.inherits(DistanceTool, baidu.lang.Class, 'DistanceTool')

    /**
     * 地图区域的移动事件绑定
     * @return 无返回值
     */
    DistanceTool.prototype._bind = function () {
        // 设置鼠标样式
        this._setCursor(this._opts.cursor)
        var me = this
        // 在装载地图的页面元素上，绑定鼠标移动事件
        baidu.on(this._map.getContainer(), 'mousemove', function (e) {
            if (!me._isOpen) {
                return
            }
            if (!me._followTitle) {
                return
            }
            e = window.event || e
            var t = e.target || e.srcElement
            // 如果触发该事件的页面元素不是遮盖效果层，则返回，无操作
            if (t != OperationMask.getDom(me._map)) {
                me._followTitle.hide()
                return
            }
            if (!me._mapMoving) {
                me._followTitle.show()
            }
            // 设置鼠标移动过程中，跟随的文字提示框的位置
            var pt = OperationMask.getDrawPoint(e, true)
            me._followTitle.setPosition(pt)
        })
        // 创建鼠标跟随的文字提示框
        if (this._startFollowText) {
            var t = this._followTitle = new BMap.Label(this._startFollowText, { offset: new BMap.Size(14, 16) })
            this._followTitle.setStyles({ color: '#333', borderColor: '#ff0103' })
        }
    }

    /**
     * 开启地图的测距状态
     * @return {Boolean}，开启测距状态成功，返回true；否则返回false。
     *
     * @example <b>参考示例：</b><br />
     * myDistanceToolObject.open();
     */
    DistanceTool.prototype.open = function () {
        // 判断测距状态是否已经开启
        if (this._isOpen == true) {
            return true
        }
        // 已有其他地图上的鼠标操作工具开启
        if (BMapLib._toolInUse) {
            return
        }
        this._isOpen = true
        BMapLib._toolInUse = true

        // 判断是否是否在移动过程中
        if (this._mapMoving) {
            delete this._mapMoving
        }

        var me = this
        // 增加鼠标在地图区域移动的事件
        // 通过binded参数，避免多次绑定
        if (!this._binded) {
            this._binded = true
            // 绑定控件项事件
            this._bind()
            // 地图的移动过程中，需要隐藏相关的提示框
            this._map.addEventListener('moving', function () {
                me._hideCurrent()
            })
        }

        // 将文字提示框作为BMap.Label元素，提交给Map Api进行管理
        if (this._followTitle) {
            this._map.addOverlay(this._followTitle)
            this._followTitle.hide()
        }

        /**
         * 测距过程中，点击地图时，触发的操作
         * @ignore
         * @param {Object} e event对象
         */
        var distClick = function (e) {
            var map = me._map
            if (!me._isOpen) {
                    return
            }
            // 通过event对象，计算得出点击位置的物理坐标，poi为一个BMap.Point对象
            e = window.event || e
            var poi = OperationMask.getDrawPoint(e, true)
            // 验证计算得出的该点的位置合理性
            if (!me._isPointValid(poi)) {
                return
            }
            // 记录当前点的屏幕位置
            me._bind.initX = e.pageX || e.clientX || 0
            me._bind.initY = e.pageY || e.clientY || 0

            // 这个if循环内的计算是，判断当前这个点，与存储内的最后一个点的距离，
            // 如果距离过小，比如小于5，可以认为是用户的误点，可以忽略掉
            if (me._points.length > 0) {
                var lstPx = map.pointToPixel(me._points[me._points.length - 1])
                var thisPx = map.pointToPixel(poi)
                var dis = Math.sqrt(Math.pow(lstPx.x - thisPx.x, 2) + Math.pow(lstPx.y - thisPx.y, 2))
                if (dis < 5) {
                    return
                }
            }

            me._bind.x = e.layerX || e.offsetX || 0
            me._bind.y = e.layerY || e.offsetY || 0
            me._points.push(poi)
            // 添加测距结点
            me._addSecPoint(poi)

            // 调整跟踪鼠标的标签
            if (me._paths.length == 0) {
                me._formatTitle(1, me._opts.followText, me._getTotalDistance())
            }

            // 修改确定线的颜色
            if (me._paths.length > 0) {
                me._paths[me._paths.length - 1].show()
                me._paths[me._paths.length - 1].setStrokeOpacity(me._opts.opacity)
            }

            var path = new BMap.Polyline([poi, poi], { enableMassClear: me._enableMassClear })
            me._map.addOverlay(path)
            me._paths.push(path)
            me._overlays.push(path)

            // 测距模式下线样式固定
            path.setStrokeWeight(me._opts.lineStroke)
            path.setStrokeColor(me._opts.lineColor)
            path.setStrokeOpacity(me._opts.opacity / 2)
            path.setStrokeStyle(me._opts.lineStyle)

            // 如果地图正在移动则隐藏掉
            if (me._mapMoving) {
                path.hide()
            }

            if (me._points.length > 1) {
                var siblingPath = me._paths[me._points.length - 2]
                siblingPath.setPositionAt(1, poi)
            }

            // 生成节点旁边的距离显示框
            var disText = ''
            if (me._points.length > 1) {
                // 非起点的节点，显示当前的距离
                var segDis = me._setSegDistance(me._points[me._points.length - 2], me._points[me._points.length - 1])
                var meters = me._getTotalDistance()
                disText = me._formatDisStr(meters)
            } else {
                disText = '起点'
            }
            var disLabel = new BMap.Label(disText, { offset: new BMap.Size(10, -5), enableMassClear: me._enableMassClear })
            disLabel.setStyles({ color: '#333', borderColor: '#ff0103' })
            me._map.addOverlay(disLabel)
            me._formatSegLabel(disLabel, disText)
            me._overlays.push(disLabel)
            poi.disLabel = disLabel
            disLabel.setPosition(poi)

            /**
             * 测距过程中，每次点击底图添加节点时，派发事件的接口
             * @name DistanceTool#onaddpoint
             * @event
             * @param {Event Object} e 回调函数会返回event参数，包括以下返回值：
             * <br />{"<b>point</b> : {BMap.Point} 最新添加上的节点BMap.Point对象,
             * <br />"<b>pixel</b>：{BMap.pixel} 最新添加上的节点BMap.Pixel对象,
             * <br />"<b>index</b>：{Number} 最新添加的节点的索引,
             * <br />"<b>distance</b>：{Number} 截止最新添加的节点的总距离}
             *
             * @example <b>参考示例：</b><br />
             * myDistanceToolObject.addEventListener("addpoint", function(e) {  alert(e.distance);  });
             */

            // 生成名为onaddpoint的baidu.lang.Event对象
            // 并给该event对象添加上point、pixel、index和distance等属性字段
            // 然后在此刻，将绑定在onaddpoint上事件，全部赋予event参数，然后派发出去
            var event = new baidu.lang.Event('onaddpoint')
            event.point = poi
            event.pixel = me._map.pointToPixel(poi)
            event.index = me._points.length - 1
            event.distance = me._getTotalDistance().toFixed(0)
            me.dispatchEvent(event)
        }

        /**
         * 测距过程中，鼠标在地图上移动时，触发的操作
         * @ignore
         * @param {Object} e event对象
         */
        var distMove = function (e) {
            if (!me._isOpen) {
                return
            }
            // 通过判断数组me._paths的长度，判断当前是否已经有测量节点
            // 也就是，如果没有节点，则还没有开始测量
            if (me._paths.length > 0) {
                // 通过event参数，计算当前点的位置
                e = window.event || e
                var curX = e.pageX || e.clientX || 0
                var curY = e.pageY || e.clientY || 0
                if (typeof me._bind.initX === 'undefined') {
                    me._bind.x = e.layerX || e.offsetX || 0
                    me._bind.y = e.layerY || e.offsetY || 0
                    me._bind.initX = curX
                    me._bind.initY = curY
                }
                var x = me._bind.x + curX - me._bind.initX
                var y = me._bind.y + curY - me._bind.initY

                // 修改最后一条折线的终点位置，使之随着鼠标移动画线
                var path = me._paths[me._paths.length - 1]
                var poi = me._map.pixelToPoint(new BMap.Pixel(x, y))
                path.setPositionAt(1, poi)

                if (!me._mapMoving) {
                    path.show()
                }
                var dx = 0
                var dy = 0
                // 计算当前鼠标位置，是否靠近边界、或者已经出了边界
                // 如果在边界位置，则需要向对应的方向移动地图，来进行测量
                // 每次移动的距离，设定为8
                if (x < 10) {
                    dx = 8
                } else if (x > me._map.getSize().width - 10) {
                    dx = -8
                }
                if (y < 10) {
                    dy = 8
                } else if (y > me._map.getSize().height - 10) {
                    dy = -8
                }
                // 如果dx和dy都等于0，表明不需要移动地图
                if (dx != 0 || dy != 0) {
                    // 此时需要向一个方向，平移地图
                    if (!distMove._movingTimerId) {
                        me._mapMoving = true
                        me._map.panBy(dx, dy, { noAnimation: true })
                        me._movingTimerId = distMove._movingTimerId = setInterval(function () {
                            me._map.panBy(dx, dy, { noAnimation: true })
                        }, 30)
                        // 地图移动过程中，隐藏线段和标签
                        path.hide()
                        me._followTitle && me._followTitle.hide()
                    }
                } else {
                    if (distMove._movingTimerId) {
                        // 此时用户不在需要移动地图来测量，可以清除计时器
                        clearInterval(distMove._movingTimerId)
                        delete distMove._movingTimerId
                        delete me._movingTimerId

                        // 显示跟随提示框，并修改线路位置
                        var lstP = me._paths[me._paths.length - 1]
                        var poiN = me._map.pixelToPoint(new BMap.Pixel(x, y))
                        if (!lstP) {
                            return
                        }
                        lstP.setPositionAt(1, poiN)
                        lstP.show()
                        if (me._followTitle) {
                            me._followTitle.setPosition(poiN)
                            me._followTitle.show()
                        }
                        me._bind.i = 0
                        me._bind.j = 0
                        delete me._mapMoving
                    }
                }
                // 实时更新文字提示框中的距离
                if (me._followTitle) {
                    var td = me._getTotalDistance()
                    var dis = me._map.getDistance(me._points[me._points.length - 1], poi)
                    me._updateInstDis(me._followTitle, td + dis)
                }
            } else {
                // 此时用户还没有开始测量，只是鼠标随便在地图上移动
                if (me._followTitle) {
                    me._followTitle.show()
                    e = window.event || e
                    var t = e.target || e.srcElement
                    if (t != OperationMask.getDom()) {
                        me._followTitle.hide()
                    }
                }
            }
        }

        /**
         * 测距要结束时，双击地图，触发的操作
         * @ignore
         * @param {Object} e event对象
         */
        var distDblclick = function (e) {
            if (!me._isOpen) {
                return
            }
            // 结束时，删除绑定的事件
            baidu.un(OperationMask.getDom(me._map), 'click', distClick)
            baidu.un(document, 'mousemove', distMove)
            baidu.un(OperationMask.getDom(me._map), 'dblclick', distDblclick)
            baidu.un(document, 'keydown', distKeyDown)
            baidu.un(OperationMask.getDom(me._map), 'mouseup', distMouseUp)

            // 调用close()关闭测距状态
            setTimeout(function () {
                me.close()
            }, 50)
        }

        /**
         * 测距时的键盘操作
         * @ignore
         * @param {Object} e event对象
         */
        var distKeyDown = function (e) {
            e = window.event || e
            if (e.keyCode == 27) {
                // [ESC]退出本次测距
                me._clearCurData()
                setTimeout(function () {
                    me.close()
                }, 50)
            }
        }

        /**
         * 测距过程中，鼠标弹起时，触发的操作
         * @ignore
         * @param {Object} e event对象
         */
        var distMouseUp = function (e) {
            e = window.event || e
            var ieVersion = 0
            if (/msie (\d+\.\d)/i.test(navigator.userAgent)) {
               ieVersion = document.documentMode || +RegExp['\x241']
            }
            if (ieVersion &&
                e.button != 1 ||
                e.button == 2) {
                    me.close()
            }
        }

        // 初始化存储数据
        me._initData()

        // 调整title的内容
        this._formatTitle()

        // 创建透明覆盖层，并设置鼠标样式
        OperationMask.show(this._map)
        this._setCursor(this._opts.cursor)

        // 绑定全部事件
        baidu.on(OperationMask.getDom(this._map), 'click', distClick)
        baidu.on(document, 'mousemove', distMove)
        baidu.on(OperationMask.getDom(this._map), 'dblclick', distDblclick)
        baidu.on(document, 'keydown', distKeyDown)
        baidu.on(OperationMask.getDom(this._map), 'mouseup', distMouseUp)

        // 将绑定的事件、和对应的绑定对象，记录在数组中
        this.bindFunc = [
            { elem: OperationMask.getDom(this._map), type: 'click', func: distClick },
            { elem: OperationMask.getDom(this._map), type: 'dblclick', func: distDblclick },
            { elem: document, type: 'mousemove', func: distMove },
            { elem: document, type: 'keydown', func: distKeyDown },
            { elem: OperationMask.getDom(this._map), type: 'mouseup', func: distMouseUp }]
        return true
    }

    /**
     * 画线结束时，派发drawend事件
     * @return 无返回值
     */
    DistanceTool.prototype._dispatchLastEvent = function () {
        /**
         * 测距时，每次双击底图结束当前测距折线时，派发事件的接口
         * @name DistanceTool#ondrawend
         * @event
         * @param {Event Object} e 回调函数会返回event参数，包括以下返回值：
         * <br />{"<b>points</b> : {BMap.Point} 所有测量时，打下的节点BMap.Point对象,
         * <br />"<b>overlays</b>：{Array} 所有测量时，生成的线段BMap.Overlay对象,
         * <br />"<b>distance</b>：{Number} 测量解释时的最终距离}
         *
         * @example <b>参考示例：</b><br />
         * myDistanceToolObject.addEventListener("drawend", function(e) {  alert(e.distance);  });
         */

        // 生成名为ondrawend的baidu.lang.Event对象
        // 并给该event对象添加上points、overlays和distance等属性字段
        // 然后在此刻，将绑定在ondrawend上事件，全部赋予event参数，然后派发出去
        var event = new baidu.lang.Event('ondrawend')
        event.points =
            this._points
                ? this._points.slice(0)
                : []
        event.overlays =
            this._paths
                ? this._paths.slice(0, this._paths.length - 1)
                : []
        event.distance = this._getTotalDistance().toFixed(0)
        this.dispatchEvent(event)
    }

    /**
     * 关闭测距状态
     * @return 无返回值
     *
     * @example <b>参考示例：</b><br />
     * myDistanceToolObject.close();
     */
    DistanceTool.prototype.close = function () {
        if (this._isOpen == false) {
            return
        }
        this._isOpen = false
        BMapLib._toolInUse = false

        if (this._mapMoving) {
            delete this._mapMoving
        }
        var me = this
        me._dispatchLastEvent()
        if (me._points.length < 2) {
            // 不是有效绘制，清除所有内容
            me._clearCurData()
        } else {
            me._paths[me._paths.length - 1].remove()
            me._paths[me._paths.length - 1] = null
            me._paths.length = me._paths.length - 1
            // 移除最近一次标记
            var pt = me._points[me._points.length - 1]
            if (pt.disLabel) {
                pt.disLabel.remove()
            }
            me._processLastOp()
        }
        OperationMask.hide()

        // 删除绑定的事件
        for (var i = 0, l = this.bindFunc.length; i < l; i++) {
            baidu.un(this.bindFunc[i].elem, this.bindFunc[i].type, this.bindFunc[i].func)
        }

        // 停止地图移动
        if (me._movingTimerId) {
            clearInterval(me._movingTimerId)
            me._movingTimerId = null
        }

        if (this._followTitle) {
            this._followTitle.hide()
        }
    }

    /**
     * 清除本次测距的暂存数据
     * @return 无返回值
     */
    DistanceTool.prototype._clearCurData = function () {
        for (var i = 0, l = this._points.length; i < l; i++) {
            if (this._points[i].disLabel) {
                this._points[i].disLabel.remove()
            }
        }
        for (var i = 0, l = this._paths.length; i < l; i++) {
            this._paths[i].remove()
        }
        for (var i = 0, l = this._dots.length; i < l; i++) {
            this._dots[i].remove()
        }
        this._initData()
    }

    /**
     * 初始化存储数组
     * @return 无返回值
     */
    DistanceTool.prototype._initData = function () {
        // 初始化point数组
        this._points.length = 0
        // 初始化path数组
        this._paths.length = 0
        // 初始化分段距离数组
        this._segDistance.length = 0
        // 初始化结点图像数组
        this._dots.length = 0
    }

    /**
     * 计算两点之间距离并存放在分段距离数组中
     * @param {Point}
     * @param {Point}
     * @return {Number} 两个地理点之间的距离
     */
    DistanceTool.prototype._setSegDistance = function (pt0, pt1) {
        if (!pt0 || !pt1) {
            return
        }
        var dis = this._map.getDistance(pt0, pt1)
        this._segDistance.push(dis)
        return dis
    }

    /**
     * 获得总距离
     * @return {Number} 总距离
     */
    DistanceTool.prototype._getTotalDistance = function () {
        var totalDis = 0
        for (var i = 0, l = this._segDistance.length; i < l; i++) {
            totalDis += this._segDistance[i]
        }
        return totalDis
    }

    /**
     * 将米制单位的数值换算成为目标单位下的数值
     * @type {Number} 需要转换的数值
     * @type {String} 字符串描述的目标单位，
     * "metric" 表示米制单位，
     * "us" 表示美国传统单位制
     * @return {Number} 转换后的数值
     */
    DistanceTool.prototype._convertUnit = function (num, unit) {
        unit = unit || 'metric'
        if (this._units[unit]) {
            return num * this._units[unit].conv
        }
        return num
    }

    /**
     * 添加测距结点
     * @param {BMap.Point} 节点
     * @return 无返回值
     */
    DistanceTool.prototype._addSecPoint = function (pt) {
        var ico =
            this._opts.secIcon
                ? this._opts.secIcon
                : new BMap.Icon('/static/bmap/images/mapctrls.png', new BMap.Size(11, 11), { imageOffset: new BMap.Size(-26, -313) })
        var secPt = new BMap.Marker(pt, {
            icon: ico,
            clickable: false,
            baseZIndex: 3500000,
            zIndexFixed: true,
            enableMassClear: this._enableMassClear
        })
        this._map.addOverlay(secPt)
        this._dots.push(secPt)
    }

    /**
     * 格式化距离字符串
     * @param {Number} 距离
     * @return {String} 格式化的字符串
     */
    DistanceTool.prototype._formatDisStr = function (distance) {
        var u = this._opts.unit
        var unit = this._units[u].u1
        var dis = this._convertUnit(distance, u)

        if (dis > this._units[u].incon) {
            dis = dis / this._units[u].incon
            unit = this._units[u].u2
            dis = dis.toFixed(1)
        } else {
            dis = dis.toFixed(0)
        }
        return dis + unit
    }

    /**
     * 设置鼠标样式
     * @param {String} cursor 鼠标样式
     * @return 没有返回值
     */
    DistanceTool.prototype._setCursor = function (cursor) {
        // 由于webkit内核浏览器下，cursor设置后默认不会居中，所以需要对偏移值进行设置
        var csr =
            /webkit/.test(navigator.userAgent.toLowerCase())
                ? 'url(' + this._opts.cursor + ') 3 6, crosshair'
                : 'url(' + this._opts.cursor + '), crosshair'
        OperationMask._setCursor(csr)
    }

    /**
     * 获取鼠标样式
     * @return {String} 跟随的鼠标样式
     */
    DistanceTool.prototype._getCursor = function () {
        return this._opts.cursor
    }

    /**
     * 调整分段距离样式
     * @param {BMap.Label} label 提示框的Label
     * @param {String} 需要填入的文字
     * @return 没有返回值
     */
    DistanceTool.prototype._formatSegLabel = function (label, text) {
        label.setStyle({ 'border': 'none', 'padding': '0' })
        label.setContent("<span style='" + this._styles.BMapLib_diso + "'><span style='" + this._styles.BMapLib_disi + "'>" + text + '</span></span>')
    }

    /**
     * 处理最后一次操作，当用户双击或测距被强行退出时调用
     * @return 没有返回值
     */
    DistanceTool.prototype._processLastOp = function () {
        var me = this
        // 删除上次移动临时数据
        delete me._bind.x
        delete me._bind.y
        delete me._bind.initX
        delete me._bind.initY
        // 验证路径
        if (me._paths.length > me._points.length - 1) {
            var l = me._paths.length - 1
            me._paths[l].remove()
            me._paths[l] = null
            me._paths.length = l
        }
        // 保存本次测距对象
        var disObj = {}
        disObj.points = me._points.slice(0)
        disObj.paths = me._paths.slice(0)
        disObj.dots = me._dots.slice(0)
        disObj.segDis = me._segDistance.slice(0)
        // 判断总距离和按钮位置
        var lstPx = me._map.pointToPixel(disObj.points[disObj.points.length - 1])
        var prePx = me._map.pointToPixel(disObj.points[disObj.points.length - 2])
        var btnOffset = [0, 0]
        var disOffset = [0, 0]
        if (lstPx.y - prePx.y >= 0) {
            // 距离位于下端
            disOffset = [-5, 11]
        } else {
            // 距离位于上端
            disOffset = [-5, -35]
        }
        if (lstPx.x - prePx.x >= 0) {
            // 按钮位于右侧
            btnOffset = [14, 0]
        } else {
            // 按钮位于左侧
            btnOffset = [-14, 0]
        }
        // 显示总距离
        var pt = disObj.points[disObj.points.length - 1]
        pt.disLabel = new BMap.Label('', { offset: new BMap.Size(-15, -40), enableMassClear: me._enableMassClear })
        pt.disLabel.setStyles({ color: '#333', borderColor: '#ff0103' })
        me._map.addOverlay(pt.disLabel)
        pt.disLabel.setOffset(new BMap.Size(disOffset[0], disOffset[1]))
        pt.disLabel.setPosition(pt)
        me._formatTitle(2, '', '', pt.disLabel)
        // 添加关闭按钮
        var bico =
            this._opts.closeIcon
                ? this._opts.closeIcon
                : new BMap.Icon('/static/bmap/images/mapctrls.gif', new BMap.Size(12, 12), { imageOffset: new BMap.Size(0, -14) })
        disObj.closeBtn = new BMap.Marker(disObj.points[disObj.points.length - 1],
            { icon: bico,
            offset: new BMap.Size(btnOffset[0], btnOffset[1]),
            baseZIndex: 3600000,
            enableMassClear: me._enableMassClear }
        )
        me._map.addOverlay(disObj.closeBtn)
        disObj.closeBtn.setTitle('清除本次测距')
        // 点击关闭按钮，绑定关闭按钮事件
        disObj.closeBtn.addEventListener('click', function (e) {
            // 关闭本次测距，清除相关存储和变量
            for (var i = 0, l = disObj.points.length; i < l; i++) {
                disObj.points[i].disLabel.remove()
                disObj.points[i].disLabel = null
            }
            for (var i = 0, l = disObj.paths.length; i < l; i++) {
                disObj.paths[i].remove()
                disObj.paths[i] = null
            }
            for (var i = 0, l = disObj.dots.length; i < l; i++) {
                disObj.dots[i].remove()
                disObj.dots[i] = null
            }
            disObj.closeBtn.remove()
            disObj.closeBtn = null
            stopBubble(e)

            /**
             * @ignore
             * 测距结束后，点击线段上最后一个节点旁的关闭按钮时，派发事件的接口
             * @name DistanceTool#onremovepolyline
             * @event
             * @param {Event Object} e 回调函数会返回event参数
             *
             * @example <b>参考示例：</b><br />
             * myDistanceToolObject.addEventListener("removepolyline", function(e) {  alert(e.type);  });
             */

            // 生成名为onremovepolyline的baidu.lang.Event对象
            // 然后在此刻，将绑定在onremovepolyline上事件，全部赋予event参数，然后派发出去
            var event = new baidu.lang.Event('onremovepolyline')
            me.dispatchEvent(event)
        })
        me._initData()
    }

    /**
     * 生成测距过程中的文字提示框
     * @param {String} type
     * @param {String} text
     * @param {String} distance
     * @param {Label} label
     * @return 无返回值
     */
    DistanceTool.prototype._formatTitle = function (type, text, distance, label) {
        var title = label || this._followTitle
        if (!title) {
            return
        }
        title.setStyle({ 'lineHeight': '16px', 'zIndex': '85', 'padding': '3px 5px' })
        var t = this._startFollowText || ''
        var htmls = []
        if (type == 1) {
            // 测距过程中的提示
            title.setOffset(0, 25)
            var u = this._opts.unit
            var unit = this._units[u].u1
            var dis = this._convertUnit(distance, u)
            if (dis > this._units[u].incon) {
                dis = dis / this._units[u].incon
                unit = this._units[u].u2
                dis = dis.toFixed(1)
            } else {
                dis = dis.toFixed(0)
            }
            htmls.push("<span>总长：<span style='" + this._styles.BMapLib_disBoxDis + "'>" + dis + '</span>' + unit + '</span><br />')
            htmls.push("<span style='color:#7a7a7a'>" + text + '</span>')
        } else if (type == 2) {
            // 结束时的总距离展示
            var u = this._opts.unit
            var unit = this._units[u].u1
            var dis = this._convertUnit(this._getTotalDistance(), u)
            if (dis > this._units[u].incon) {
                dis = dis / this._units[u].incon
                unit = this._units[u].u2
                dis = dis.toFixed(1)
            } else {
                dis = dis.toFixed(0)
            }
            htmls.push("总长：<span style='" + this._styles.BMapLib_disBoxDis + "'>" + dis + '</span>' + unit)
        } else {
            title.setOffset(0, 25)
            htmls.push(t)
        }
        title.setContent(htmls.join(''))
    }

    /**
     * 更新label的距离
     * @param HTMLElement label的DOM元素
     * @param Number 距离
     */
    DistanceTool.prototype._updateInstDis = function (label, dis) {
        // 换算距离
        var u = this._opts.unit
        var unit = this._units[u].u1
        if (dis > this._units[u].incon) {
            dis = dis / this._units[u].incon
            unit = this._units[u].u2
            dis = dis.toFixed(1)
        } else {
            dis = dis.toFixed(0)
        }
        // 修改Label的内容
        if (label) {
            var htmls = []
            htmls.push("<span>总长：<span style='" + this._styles.BMapLib_disBoxDis + "'>" + dis + '</span>' + unit + '</span><br />')
            htmls.push("<span style='color:#7a7a7a'>" + this._opts.followText + '</span>')
            label.setContent(htmls.join(''))
        }
    }

    /**
     * 隐藏相关的线段和提示框文字
     * @return 无返回值
     */
    DistanceTool.prototype._hideCurrent = function () {
        if (!this._isOpen) {
            return
        }
        if (this._paths.length > 0) {
            var p = this._paths[this._paths.length - 1]
            p.hide()
        }
        this._followTitle && this._followTitle.hide()
    }

    /**
     * 验证传入点的位置合理性
     * @param {BMap.Point} pt 需要被验证的point点
     * @return 无返回值
     */
    DistanceTool.prototype._isPointValid = function (pt) {
        if (!pt) {
            return false
        }
        var mapBounds = this._map.getBounds()
        var sw = mapBounds.getSouthWest()
              var ne = mapBounds.getNorthEast()
        if (pt.lng < sw.lng ||
            pt.lng > ne.lng ||
            pt.lat < sw.lat ||
            pt.lat > ne.lat) {
                return false
        }
        return true
    }

    /**
     * OperationMask，透明覆盖层，在地图上进行鼠标绘制操作时使用，
     * 闭包，对外不暴露
     */
    var OperationMask = {
        /**
         * map对象
         * @type {Map}
         */
        _map: null,

        /**
         * HTML字符串
         * @type {String}
         */
        _html: "<div style='background:transparent url(/static/bmap/images/blank.gif);position:absolute;left:0;top:0;width:100%;height:100%;z-index:1000' unselectable='on'></div>",

        /**
         * html元素
         * @type {HTMLElement}
         */
        _maskElement: null,

        /**
         * 鼠标指针
         * @type {String}
         */
        _cursor: 'default',

        /**
         * 操作层是否在使用中
         * @type {Boolean}
         */
        _inUse: false,

        /**
         * 透明覆盖层的显示
         *
         * @param {Map} map map对象
         * @return 无返回值
         */
        show: function (map) {
            if (!this._map) {
                this._map = map
            }
            this._inUse = true
            if (!this._maskElement) {
                this._createMask(map)
            }
            this._maskElement.style.display = 'block'
        },

        /**
         * 创建覆盖层
         *
         * @param {Map} map map对象
         * @return 无返回值
         */
        _createMask: function (map) {
            this._map = map
            if (!this._map) {
                return
            }
            baidu.insertHTML(this._map.getContainer(), 'beforeEnd', this._html)
            var elem = this._maskElement = this._map.getContainer().lastChild

            var stopAndPrevent = function (e) {
                stopBubble(e)
                return baidu.preventDefault(e)
            }
            baidu.on(elem, 'mouseup', function (e) {
                if (e.button == 2) {
                    stopAndPrevent(e)
                }
            })
            baidu.on(elem, 'contextmenu', stopAndPrevent)
            elem.style.display = 'none'
        },

        /**
         * 获取当前绘制点的地理坐标
         *
         * @param {Event} e e对象
         * @param {Boolean} n 是否向上查到相对于地图container元素的坐标位置
         * @return Point对象的位置信息
         */
        getDrawPoint: function (e, n) {
            e = window.event || e
            var x = e.layerX || e.offsetX || 0
            var y = e.layerY || e.offsetY || 0
            var t = e.target || e.srcElement
            if (t != OperationMask.getDom(this._map) && n == true) {
                while (t && t != this._map.getContainer()) {
                    if (!(t.clientWidth == 0 &&
                         t.clientHeight == 0 &&
                         t.offsetParent &&
                         t.offsetParent.nodeName.toLowerCase() == 'td')) {
                            x += t.offsetLeft
                            y += t.offsetTop
                    }
                    t = t.offsetParent
                }
            }

            if (t != OperationMask.getDom(this._map) &&
                t != this._map.getContainer()) {
                    return
            }
            if (typeof x === 'undefined' ||
                typeof y === 'undefined') {
                    return
            }
            if (isNaN(x) || isNaN(y)) {
                return
            }
            return this._map.pixelToPoint(new BMap.Pixel(x, y))
        },

        /**
         * 透明覆盖层的隐藏
         *
         * @return 无返回值
         */
        hide: function () {
            if (!this._map) {
                return
            }
            this._inUse = false
            if (this._maskElement) {
                this._maskElement.style.display = 'none'
            }
        },

        /**
         * 获取HTML容器
         *
         * @param {Map} map map对象
         * @return HTML容器元素
         */
        getDom: function (map) {
            if (!this._maskElement) {
                this._createMask(map)
            }
            return this._maskElement
        },

        /**
         * 设置鼠标样式
         *
         * @type {String} cursor 鼠标样式
         * @return 无返回值
         */
        _setCursor: function (cursor) {
            this._cursor = cursor || 'default'
            if (this._maskElement) {
                this._maskElement.style.cursor = this._cursor
            }
        }
    }

    /**
     * 停止事件冒泡传播，
     * 闭包，对外不暴露
     *
     * @type {Event} e e对象
     */
    function stopBubble (e) {
        var e = window.event || e
        e.stopPropagation ? e.stopPropagation() : e.cancelBubble = true
    };
})()
